package core;

import java.util.UUID;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * ID生成器工具类
 * <p>
 * 提供各种ID生成方法，包括UUID、雪花算法等。
 * </p>
 */
public class IdUtil {

    // ------------------------------------------------------------------- UUID

    /**
     * 获取随机UUID
     *
     * @return 随机UUID
     */
    public static String randomUUID() {
        return UUID.randomUUID().toString();
    }

    /**
     * 简化的UUID，去掉了横线
     *
     * @return 简化的UUID，去掉了横线
     */
    public static String simpleUUID() {
        return randomUUID().replace("-", "");
    }

    /**
     * 获取随机UUID，使用性能更好的ThreadLocalRandom生成UUID
     *
     * @return 随机UUID
     */
    public static String fastUUID() {
        return randomUUID();
    }

    /**
     * 简化的UUID，去掉了横线，使用性能更好的ThreadLocalRandom生成UUID
     *
     * @return 简化的UUID，去掉了横线
     */
    public static String fastSimpleUUID() {
        return fastUUID().replace("-", "");
    }

    // ------------------------------------------------------------------- Snowflake

    /**
     * 创建Snowflake生成器
     *
     * @param workerId     终端ID
     * @param datacenterId 数据中心ID
     * @return {@link Snowflake}
     */
    public static Snowflake createSnowflake(long workerId, long datacenterId) {
        return new Snowflake(workerId, datacenterId);
    }

    /**
     * 创建Snowflake生成器
     *
     * @return {@link Snowflake}
     */
    public static Snowflake createSnowflake() {
        return new Snowflake();
    }

    /**
     * 获取下一个ID
     *
     * @return ID
     */
    public static long getSnowflakeNextId() {
        return Snowflake.getInstance().nextId();
    }

    /**
     * 获取下一个ID字符串
     *
     * @return ID字符串
     */
    public static String getSnowflakeNextIdStr() {
        return String.valueOf(getSnowflakeNextId());
    }

    // ------------------------------------------------------------------- ObjectId

    /**
     * 创建ObjectId
     *
     * @return ObjectId
     */
    public static String objectId() {
        return ObjectId.next();
    }

    // ------------------------------------------------------------------- NanoId

    /**
     * 创建NanoId
     *
     * @return NanoId
     */
    public static String nanoId() {
        return NanoId.randomNanoId();
    }

    /**
     * 创建NanoId
     *
     * @param size ID长度
     * @return NanoId
     */
    public static String nanoId(int size) {
        return NanoId.randomNanoId(size);
    }

    // ------------------------------------------------------------------- 内部类

    /**
     * Snowflake ID生成器
     */
    public static class Snowflake {
        private static final Snowflake INSTANCE = new Snowflake();

        // 起始时间戳常量，2020-01-01 00:00:00
        private static final long START_TIMESTAMP = 1577836800000L;

        // 机器ID所占位数
        private static final long WORKER_ID_BITS = 5L;
        // 数据中心ID所占位数
        private static final long DATACENTER_ID_BITS = 5L;
        // 序列号所占位数
        private static final long SEQUENCE_BITS = 12L;

        // 机器ID最大值
        private static final long MAX_WORKER_ID = ~(-1L << WORKER_ID_BITS);
        // 数据中心ID最大值
        private static final long MAX_DATACENTER_ID = ~(-1L << DATACENTER_ID_BITS);
        // 序列号最大值
        private static final long MAX_SEQUENCE = ~(-1L << SEQUENCE_BITS);

        // 机器ID向左偏移位数
        private static final long WORKER_ID_SHIFT = SEQUENCE_BITS;
        // 数据中心ID向左偏移位数
        private static final long DATACENTER_ID_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS;
        // 时间戳向左偏移位数
        private static final long TIMESTAMP_LEFT_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS + DATACENTER_ID_BITS;

        // 工作机器ID (0~31)
        private final long workerId;
        // 数据中心ID (0~31)
        private final long datacenterId;
        // 毫秒内序列 (0~4095)
        private long sequence = 0L;
        // 上次生成ID的时间戳
        private long lastTimestamp = -1L;

        /**
         * 构造函数
         */
        public Snowflake() {
            this(1, 1);
        }

        /**
         * 构造函数
         *
         * @param workerId     工作ID (0~31)
         * @param datacenterId 数据中心ID (0~31)
         */
        public Snowflake(long workerId, long datacenterId) {
            if (workerId > MAX_WORKER_ID || workerId < 0) {
                throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", MAX_WORKER_ID));
            }
            if (datacenterId > MAX_DATACENTER_ID || datacenterId < 0) {
                throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", MAX_DATACENTER_ID));
            }
            this.workerId = workerId;
            this.datacenterId = datacenterId;
        }

        /**
         * 获取单例实例
         *
         * @return Snowflake实例
         */
        public static Snowflake getInstance() {
            return INSTANCE;
        }

        /**
         * 获得下一个ID
         *
         * @return 下一个ID
         */
        public synchronized long nextId() {
            long timestamp = System.currentTimeMillis();

            // 如果当前时间小于上一次ID生成的时间戳，说明系统时钟回退过，此时应该抛出异常
            if (timestamp < lastTimestamp) {
                throw new RuntimeException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
            }

            // 如果是同一时间生成的，则进行毫秒内序列递增
            if (lastTimestamp == timestamp) {
                sequence = (sequence + 1) & MAX_SEQUENCE;
                // 毫秒内序列溢出
                if (sequence == 0) {
                    // 阻塞到下一个毫秒，直到获得新的时间戳
                    timestamp = tilNextMillis(lastTimestamp);
                }
            } else {
                // 时间戳改变，毫秒内序列重置
                sequence = 0L;
            }

            // 上次生成ID的时间戳
            lastTimestamp = timestamp;

            // 移位并通过或运算拼到一起组成64位的ID
            return ((timestamp - START_TIMESTAMP) << TIMESTAMP_LEFT_SHIFT)
                    | (datacenterId << DATACENTER_ID_SHIFT)
                    | (workerId << WORKER_ID_SHIFT)
                    | sequence;
        }

        /**
         * 阻塞到下一个毫秒，直到获得新的时间戳
         *
         * @param lastTimestamp 上次生成ID的时间戳
         * @return 下一个时间戳
         */
        protected long tilNextMillis(long lastTimestamp) {
            long timestamp = System.currentTimeMillis();
            while (timestamp <= lastTimestamp) {
                timestamp = System.currentTimeMillis();
            }
            return timestamp;
        }
    }

    /**
     * ObjectId生成器
     */
    public static class ObjectId {
        private static final int RANDOM_VALUE1 = ThreadLocalRandom.current().nextInt(0x03FFFFFF);
        private static final short RANDOM_VALUE2 = (short) ThreadLocalRandom.current().nextInt(0x7FFF);

        private static final AtomicInteger NEXT_COUNTER = new AtomicInteger(ThreadLocalRandom.current().nextInt(0xFFFFFF));

        /**
         * 生成下一个ObjectId
         *
         * @return ObjectId字符串
         */
        public static String next() {
            return toHexString(timestamp(), 8) +
                    toHexString(RANDOM_VALUE1, 6) +
                    toHexString(RANDOM_VALUE2, 4) +
                    toHexString(nextCounter(), 6);
        }

        private static int timestamp() {
            return (int) (System.currentTimeMillis() / 1000);
        }

        private static int nextCounter() {
            return NEXT_COUNTER.getAndIncrement() & 0xFFFFFF;
        }

        private static String toHexString(int value, int width) {
            String hex = Integer.toHexString(value);
            StringBuilder sb = new StringBuilder(width);
            for (int i = 0; i < width - hex.length(); i++) {
                sb.append('0');
            }
            sb.append(hex);
            return sb.toString();
        }
    }

    /**
     * NanoId生成器
     */
    public static class NanoId {
        private static final char[] ALPHABET =
                "ModuleSymbhasOwnPr-0123456789ABCDEFGHNRVfgctiUvz_KqYTJkLxpZXIjQW".toCharArray();

        private static final int DEFAULT_SIZE = 21;

        /**
         * 生成随机NanoId
         *
         * @return NanoId字符串
         */
        public static String randomNanoId() {
            return randomNanoId(DEFAULT_SIZE);
        }

        /**
         * 生成指定长度的随机NanoId
         *
         * @param size ID长度
         * @return NanoId字符串
         */
        public static String randomNanoId(int size) {
            if (size < 1) {
                throw new IllegalArgumentException("Size must be greater than 0.");
            }

            final int mask = (2 << (int) Math.floor(Math.log(ALPHABET.length - 1) / Math.log(2))) - 1;
            final int step = (int) Math.ceil(1.6 * mask * size / ALPHABET.length);

            final StringBuilder idBuilder = new StringBuilder(size);
            final byte[] bytes = new byte[step];

            while (idBuilder.length() < size) {
                ThreadLocalRandom.current().nextBytes(bytes);
                for (int i = 0; i < step; i++) {
                    final int index = bytes[i] & mask;
                    if (index < ALPHABET.length) {
                        idBuilder.append(ALPHABET[index]);
                        if (idBuilder.length() == size) {
                            break;
                        }
                    }
                }
            }

            return idBuilder.toString();
        }
    }
}